/**
 * Copyright (c) 2005 - 2010, James Auldridge
 * All rights reserved.
 *
 * Licensed under the BSD, MIT, and GPL (your choice!) Licenses:
 *  http://code.google.com/p/cookies/wiki/License
 *
 */
var jaaulde = window.jaaulde || {};
jaaulde.utils = jaaulde.utils || {};
jaaulde.utils.cookies = ( function()
{
    var resolveOptions, assembleOptionsString, parseCookies, constructor, defaultOptions = {
        expiresAt: null,
        path: '/',
        domain:  null,
        secure: false
    };
    /**
	* resolveOptions - receive an options object and ensure all options are present and valid, replacing with defaults where necessary
	*
	* @access private
	* @static
	* @parameter Object options - optional options to start with
	* @return Object complete and valid options object
	*/
    resolveOptions = function( options )
    {
        var returnValue, expireDate;

        if( typeof options !== 'object' || options === null )
        {
            returnValue = defaultOptions;
        }
        else
        {
            returnValue = {
                expiresAt: defaultOptions.expiresAt,
                path: defaultOptions.path,
                domain: defaultOptions.domain,
                secure: defaultOptions.secure
            };

            if( typeof options.expiresAt === 'object' && options.expiresAt instanceof Date )
            {
                returnValue.expiresAt = options.expiresAt;
            }
            else if( typeof options.hoursToLive === 'number' && options.hoursToLive !== 0 )
            {
                expireDate = new Date();
                expireDate.setTime( expireDate.getTime() + ( options.hoursToLive * 60 * 60 * 1000 ) );
                returnValue.expiresAt = expireDate;
            }

            if( typeof options.path === 'string' && options.path !== '' )
            {
                returnValue.path = options.path;
            }

            if( typeof options.domain === 'string' && options.domain !== '' )
            {
                returnValue.domain = options.domain;
            }

            if( options.secure === true )
            {
                returnValue.secure = options.secure;
            }
        }

        return returnValue;
    };
    /**
	* assembleOptionsString - analyze options and assemble appropriate string for setting a cookie with those options
	*
	* @access private
	* @static
	* @parameter options OBJECT - optional options to start with
	* @return STRING - complete and valid cookie setting options
	*/
    assembleOptionsString = function( options )
    {
        options = resolveOptions( options );

        return (
            ( typeof options.expiresAt === 'object' && options.expiresAt instanceof Date ? '; expires=' + options.expiresAt.toGMTString() : '' ) +
            '; path=' + options.path +
            ( typeof options.domain === 'string' ? '; domain=' + options.domain : '' ) +
            ( options.secure === true ? '; secure' : '' )
            );
    };
    /**
	* parseCookies - retrieve document.cookie string and break it into a hash with values decoded and unserialized
	*
	* @access private
	* @static
	* @return OBJECT - hash of cookies from document.cookie
	*/
    parseCookies = function()
    {
        var cookies = {}, i, pair, name, value, separated = document.cookie.split( ';' ), unparsedValue;
        for( i = 0; i < separated.length; i = i + 1 )
        {
            pair = separated[i].split( '=' );
            name = pair[0].replace( /^\s*/, '' ).replace( /\s*$/, '' );

            try
            {
                value = decodeURIComponent( pair[1] );
            }
            catch( e1 )
            {
                value = pair[1];
            }

            if( typeof JSON === 'object' && JSON !== null && typeof JSON.parse === 'function' )
            {
                try
                {
                    unparsedValue = value;
                    value = JSON.parse( value );
                }
                catch( e2 )
                {
                    value = unparsedValue;
                }
            }

            cookies[name] = value;
        }
        return cookies;
    };

    constructor = function(){};

    /**
	 * get - get one, several, or all cookies
	 *
	 * @access public
	 * @paramater Mixed cookieName - String:name of single cookie; Array:list of multiple cookie names; Void (no param):if you want all cookies
	 * @return Mixed - Value of cookie as set; Null:if only one cookie is requested and is not found; Object:hash of multiple or all cookies (if multiple or all requested);
	 */
    constructor.prototype.get = function( cookieName )
    {
        var returnValue, item, cookies = parseCookies();

        if( typeof cookieName === 'string' )
        {
            returnValue = ( typeof cookies[cookieName] !== 'undefined' ) ? cookies[cookieName] : null;
        }
        else if( typeof cookieName === 'object' && cookieName !== null )
        {
            returnValue = {};
            for( item in cookieName )
            {
                if( typeof cookies[cookieName[item]] !== 'undefined' )
                {
                    returnValue[cookieName[item]] = cookies[cookieName[item]];
                }
                else
                {
                    returnValue[cookieName[item]] = null;
                }
            }
        }
        else
        {
            returnValue = cookies;
        }

        return returnValue;
    };
    /**
	 * filter - get array of cookies whose names match the provided RegExp
	 *
	 * @access public
	 * @paramater Object RegExp - The regular expression to match against cookie names
	 * @return Mixed - Object:hash of cookies whose names match the RegExp
	 */
    constructor.prototype.filter = function( cookieNameRegExp )
    {
        var cookieName, returnValue = {}, cookies = parseCookies();

        if( typeof cookieNameRegExp === 'string' )
        {
            cookieNameRegExp = new RegExp( cookieNameRegExp );
        }

        for( cookieName in cookies )
        {
            if( cookieName.match( cookieNameRegExp ) )
            {
                returnValue[cookieName] = cookies[cookieName];
            }
        }

        return returnValue;
    };
    /**
	 * set - set or delete a cookie with desired options
	 *
	 * @access public
	 * @paramater String cookieName - name of cookie to set
	 * @paramater Mixed value - Any JS value. If not a string, will be JSON encoded; NULL to delete
	 * @paramater Object options - optional list of cookie options to specify
	 * @return void
	 */
    constructor.prototype.set = function( cookieName, value, options )
    {
        if( typeof options !== 'object' || options === null )
        {
            options = {};
        }

        if( typeof value === 'undefined' || value === null )
        {
            value = '';
            options.hoursToLive = -8760;
        }

        else if( typeof value !== 'string' )
        {
            if( typeof JSON === 'object' && JSON !== null && typeof JSON.stringify === 'function' )
            {
                value = JSON.stringify( value );
            }
            else
            {
                throw new Error( 'cookies.set() received non-string value and could not serialize.' );
            }
        }


        var optionsString = assembleOptionsString( options );

        document.cookie = cookieName + '=' + encodeURIComponent( value ) + optionsString;
    };
    /**
	 * del - delete a cookie (domain and path options must match those with which the cookie was set; this is really an alias for set() with parameters simplified for this use)
	 *
	 * @access public
	 * @paramater MIxed cookieName - String name of cookie to delete, or Bool true to delete all
	 * @paramater Object options - optional list of cookie options to specify ( path, domain )
	 * @return void
	 */
    constructor.prototype.del = function( cookieName, options )
    {
        var allCookies = {}, name;

        if( typeof options !== 'object' || options === null )
        {
            options = {};
        }

        if( typeof cookieName === 'boolean' && cookieName === true )
        {
            allCookies = this.get();
        }
        else if( typeof cookieName === 'string' )
        {
            allCookies[cookieName] = true;
        }

        for( name in allCookies )
        {
            if( typeof name === 'string' && name !== '' )
            {
                this.set( name, null, options );
            }
        }
    };
    /**
	 * test - test whether the browser is accepting cookies
	 *
	 * @access public
	 * @return Boolean
	 */
    constructor.prototype.test = function()
    {
        var returnValue = false, testName = 'cT', testValue = 'data';

        this.set( testName, testValue );

        if( this.get( testName ) === testValue )
        {
            this.del( testName );
            returnValue = true;
        }

        return returnValue;
    };
    /**
	 * setOptions - set default options for calls to cookie methods
	 *
	 * @access public
	 * @param Object options - list of cookie options to specify
	 * @return void
	 */
    constructor.prototype.setOptions = function( options )
    {
        if( typeof options !== 'object' )
        {
            options = null;
        }

        defaultOptions = resolveOptions( options );
    };

    return new constructor();
} )();

( function()
{
    if( window.jQuery )
    {
        ( function( $ )
        {
            $.cookies = jaaulde.utils.cookies;

            var extensions = {
                /**
				* $( 'selector' ).cookify - set the value of an input field, or the innerHTML of an element, to a cookie by the name or id of the field or element
				*                           (field or element MUST have name or id attribute)
				*
				* @access public
				* @param options OBJECT - list of cookie options to specify
				* @return jQuery
				*/
                cookify: function( options )
                {
                    return this.each( function()
                    {
                        var i, nameAttrs = ['name', 'id'], name, $this = $( this ), value;

                        for( i in nameAttrs )
                        {
                            if( ! isNaN( i ) )
                            {
                                name = $this.attr( nameAttrs[ i ] );
                                if( typeof name === 'string' && name !== '' )
                                {
                                    if( $this.is( ':checkbox, :radio' ) )
                                    {
                                        if( $this.attr( 'checked' ) )
                                        {
                                            value = $this.val();
                                        }
                                    }
                                    else if( $this.is( ':input' ) )
                                    {
                                        value = $this.val();
                                    }
                                    else
                                    {
                                        value = $this.html();
                                    }

                                    if( typeof value !== 'string' || value === '' )
                                    {
                                        value = null;
                                    }

                                    $.cookies.set( name, value, options );

                                    break;
                                }
                            }
                        }
                    } );
                },
                /**
				* $( 'selector' ).cookieFill - set the value of an input field or the innerHTML of an element from a cookie by the name or id of the field or element
				*
				* @access public
				* @return jQuery
				*/
                cookieFill: function()
                {
                    return this.each( function()
                    {
                        var n, getN, nameAttrs = ['name', 'id'], name, $this = $( this ), value;

                        getN = function()
                        {
                            n = nameAttrs.pop();
                            return !! n;
                        };

                        while( getN() )
                        {
                            name = $this.attr( n );
                            if( typeof name === 'string' && name !== '' )
                            {
                                value = $.cookies.get( name );
                                if( value !== null )
                                {
                                    if( $this.is( ':checkbox, :radio' ) )
                                    {
                                        if( $this.val() === value )
                                        {
                                            $this.attr( 'checked', 'checked' );
                                        }
                                        else
                                        {
                                            $this.removeAttr( 'checked' );
                                        }
                                    }
                                    else if( $this.is( ':input' ) )
                                    {
                                        $this.val( value );
                                    }
                                    else
                                    {
                                        $this.html( value );
                                    }
                                }
								
                                break;
                            }
                        }
                    } );
                },
                /**
				* $( 'selector' ).cookieBind - call cookie fill on matching elements, and bind their change events to cookify()
				*
				* @access public
				* @param options OBJECT - list of cookie options to specify
				* @return jQuery
				*/
                cookieBind: function( options )
                {
                    return this.each( function()
                    {
                        var $this = $( this );
                        $this.cookieFill().change( function()
                        {
                            $this.cookify( options );
                        } );
                    } );
                }
            };

            $.each( extensions, function( i )
            {
                $.fn[i] = this;
            } );

        } )( window.jQuery );
    }
} )();